package de.uhlesoluschns.opensearch.provider;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.net.URLCodec;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import android.content.ContentValues;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;

public class XmlImporter {
	
	private static final String NS_OSD = "http://a9.com/-/spec/opensearch/1.1/";
	
	private DocumentBuilder builder;

	public XmlImporter() {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		factory.setExpandEntityReferences(false);
		factory.setIgnoringComments(true);
		factory.setIgnoringElementContentWhitespace(true);
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public ContentValues parse(Uri data) throws IOException, SAXException {
		InputStream in = null;
		try {
			URL url = new URL(data.toString());
			in = url.openStream();
			Document doc = builder.parse(in);
			return parseOpenSearchDescription(doc.getDocumentElement());
		} catch (IOException e) {
			throw e;
		} catch (SAXException e) {
			throw e;
		} finally {
			try {
				in.close();
			} catch (Exception e) {}
		}
	}
	
	private enum OsdTags {
		ShortName,
		Description,
		InputEncoding,
		Image,
		Url
	}
	
	private static final String TYPE_SEARCH = "text/html";
	private static final String TYPE_SUGGESTION = "application/x-suggestions+json";
	
	private ContentValues parseOpenSearchDescription(Element root) {
		ContentValues engine = new ContentValues();
		for(Node childNode = root.getFirstChild(); childNode != null; childNode = childNode.getNextSibling()) {
			if (childNode instanceof Element) {
				String ns = childNode.getNamespaceURI();
				//XXX force correct NS?
				if (true || ns == null || NS_OSD.equals(ns)) {
					Element elt = (Element) childNode;
					try {
						OsdTags tag = OsdTags.valueOf(elt.getLocalName());
						switch (tag) {
						case ShortName:
							engine.put("shortName", elt.getFirstChild()
									.getNodeValue().trim());
							break;
						case Description:
							engine.put("description", elt.getFirstChild()
									.getNodeValue().trim());
							break;
						case InputEncoding:
							engine.put("inputEncoding", elt.getFirstChild()
									.getNodeValue().trim());
							break;
						case Image:
							engine.put("image", elt.getFirstChild()
									.getNodeValue().trim());
							break;
						case Url:
							String type = elt.getAttribute("type").trim();
							String template = elt.getAttribute("template")
									.trim();

							Uri url = Uri.parse(template);
							Uri.Builder builder = url.buildUpon();

							//read params
							for (Node paramNode = elt.getFirstChild(); paramNode != null; paramNode = paramNode
									.getNextSibling()) {
								if (paramNode instanceof Element) {
									Element paramElt = (Element) paramNode;
									if ("Param".equals(paramElt.getTagName())) {
										String paramName = paramElt
												.getAttribute("name").trim();
										String paramValue = paramElt
												.getAttribute("value").trim();
										builder.appendQueryParameter(paramName,
												paramValue);
									}
								}
							}
							url = builder.build();

							if (TYPE_SEARCH.equals(type)) {
								String method = elt.hasAttribute("method") ? elt
										.getAttribute("method")
										: null;
								if (method == null
										|| "get".equals(method.toLowerCase())) {
									engine.put("searchUrl",
											replaceSearchVariables(url
													.toString()));
								} else {
									//TODO notify user
								}
							} else if (TYPE_SUGGESTION.equals(type)) {
								engine.put("suggestionUrl",
										replaceSearchVariables(url.toString()));
							}
							break;
						}
					} catch (IllegalArgumentException e) {/* Unknown Tag, that's OK */}
				}
			}
		}
		return engine;
	}
	
	private String replaceSearchVariables(String in) {
		in = in.replaceAll("\\{searchTerms\\}", "%s");
		in = in.replaceAll("%7BsearchTerms%7D", "%s");
		in = in.replaceAll("\\{.*\\}", "");
		in = in.replaceAll("%7B.*%7D", "");
		return in;
	}
	
	public Bitmap loadImage(String source) throws MalformedURLException, DecoderException {
		if(source == null) {
			return null;
		}
		if(source.startsWith("http")) {
			try {
				URL iconUrl = new URL(source);
				InputStream is = iconUrl.openStream();
				return BitmapFactory.decodeStream(is);
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				
			}
		} else if(source.startsWith("data")) {
			//base 64
			int pos = source.indexOf("base64,");
			if(pos > 0) {
				pos += "base64,".length();
				byte [] data = new byte[source.length() - pos];
				source.getBytes(pos, source.length(), data, 0);
				data = Base64.decodeBase64(data);
				return BitmapFactory.decodeByteArray(data, 0, data.length);
			} else {
				//url encoded binary data
				pos = source.indexOf(",");
				if(pos > 0) {
					pos += ",".length();
					byte [] data = new byte[source.length() - pos];
					source.getBytes(pos, source.length(), data, 0);
					data = URLCodec.decodeUrl(data);
					return BitmapFactory.decodeByteArray(data, 0, data.length);
				}
			}
		}
		return null;
	}
}
